package com.cvte.vdt.uartservice.util;

import android.os.Handler;
import android.os.Message;

import com.cvte.vdt.uartservice.serial.SerialPortUtil;

import java.util.ArrayDeque;
import java.util.PriorityQueue;
import java.util.Queue;

/**
 * Created by user on 2018/12/10.
 */
public class BtTasksUtil{
    private final static String TAG = "BtTasksUtil";
    private final static int CLEAN_GET_NAME_MSG = 0;
    private final static int CLEAN_GET_NAME_TIME = 60000;
    private final static byte DEF_CMD_ID = 0x00;
    private final static long DEFTIMEOUT = 2000;
    private static boolean isInit =false;
    private static Queue<BtTaskPara> taskQueue;
    private static Queue<String> macQueue;
    private static TasksThread tasksThread;
    private static volatile boolean run;
    private static SerialPortUtil serialPortUtil;
    private static volatile TASKSTATUS taskstatus;
    private static byte curCmdID = DEF_CMD_ID;
    private static BtTaskPara curPara = null;
    private static OnTaskCallback callback = null;
    private static Handler handler = new Handler(){
        @Override
        public void handleMessage(Message msg) {
            switch (msg.what){
                case CLEAN_GET_NAME_MSG:
                    macQueue.clear();
                    break;
            }
        }
    };

    public static void initTasks(SerialPortUtil.OnDataReceiveListener dataReceiveListener,OnTaskCallback tmpCallback){
        serialPortUtil = SerialPortUtil.getInstance();
        serialPortUtil.setOnDataReceiveListener(dataReceiveListener);
        callback = tmpCallback;
        taskQueue = new PriorityQueue<BtTaskPara>();
        taskQueue.clear();
        macQueue = new ArrayDeque<>();
        macQueue.clear();
        taskstatus = TASKSTATUS.FREE;
        run = true;
        tasksThread = new TasksThread();
        tasksThread.start();
        isInit = true;
    }
    public static void deinitTasks(){
        isInit = false;
        run = false;
        if(handler.hasMessages(CLEAN_GET_NAME_MSG)){
            handler.removeMessages(CLEAN_GET_NAME_MSG);
        }
        taskQueue.clear();
        macQueue.clear();
        serialPortUtil.closeSerialPort();
        tasksThread.interrupt();
    }
    public static void addTask(BtTaskPara para){
        if(isInit) {
            taskQueue.add(para);
            if (taskQueue.size() == 1 && taskstatus == TASKSTATUS.FREE) {
                LogUtil.d(TAG," taskstatus == TASKSTATUS.FREE  tasksThread.interrupt()");
                tasksThread.interrupt();
            }
        }
    }

    public static void addTask(byte[] cmd, PRIORITY priority,long timeout){
        if(isInit) {
            BtTaskPara para = new BtTaskPara(cmd, priority, timeout);
            taskQueue.add(para);
            if (taskQueue.size() == 1 && taskstatus == TASKSTATUS.FREE) {
                LogUtil.d(TAG," taskstatus == TASKSTATUS.FREE  tasksThread.interrupt()");
                tasksThread.interrupt();
            }
        }
    }

    public static void addTask(byte[] cmd, PRIORITY priority){
        addTask(cmd,priority,DEFTIMEOUT);
    }

    public static void addTask(byte[] cmd){
        addTask(cmd,PRIORITY.MID,DEFTIMEOUT);
    }

    public static void addTask(byte[] cmd,long timeout){
        addTask(cmd,PRIORITY.MID,timeout);
    }

    public static void finishCurTask(){
        if(isInit && taskstatus == TASKSTATUS.BUSY){
            LogUtil.d(TAG,"  finishCurTask  curCmdID = " + curCmdID);
            tasksThread.interrupt();
        }
    }

    public static void finishTaskByID(byte cmdID){
        if(isInit && taskstatus == TASKSTATUS.BUSY){
            if(curCmdID == cmdID) {
                LogUtil.d(TAG,"  finishTaskByID  curCmdID = " + curCmdID );
                tasksThread.interrupt();
            }
        }
    }

    public static void cleanTasks(){
        taskQueue.clear();
        finishCurTask();
    }

    public static byte[] getCurParaData(){
        if(curPara != null){
            return  curPara.getData();
        }
        return null;
    }

    public static void addFIFOMac(String mac){
        macQueue.add(mac);
        if(handler.hasMessages(CLEAN_GET_NAME_MSG)){
            handler.removeMessages(CLEAN_GET_NAME_MSG);
        }
        handler.sendEmptyMessageDelayed(CLEAN_GET_NAME_MSG,CLEAN_GET_NAME_TIME);
    }

    public static String getFIFOMac(){
        if(macQueue.isEmpty()){
            return DataUtil.DEFAULT_MAC;
        }
        return macQueue.poll();
    }

    public static void cleanFIFOMac(){
        macQueue.clear();
    }

    public enum PRIORITY{
        LOW,
        MID,
        HIGH
    };
    public enum TASKSTATUS{
        FREE,
        BUSY
    }
    public static class BtTaskPara implements Comparable<BtTaskPara> ,Cloneable{
        private byte[] cmd;
        private byte[] data;
        private PRIORITY priority;
        private long timeout;
        private byte cmdID;

        public BtTaskPara(byte[] cmd, PRIORITY priority,long timeout) {
            this.cmd = cmd;
            this.priority = priority;
            this.timeout = timeout;
            if(cmd.length > 3){
                cmdID = cmd[2];
            }else {
                cmdID = 0x00;
            }
            if(cmd.length > 5){
                int length = cmd[1];
                data = new byte[length -1];
                System.arraycopy(cmd, 3, data, 0, length - 1);
            }else {
                data = null;
            }
        }

        public byte getCmdID() {
            return cmdID;
        }

        public BtTaskPara(byte[] cmd, PRIORITY priority) {
            this(cmd,priority,DEFTIMEOUT);
        }

        public BtTaskPara(byte[] cmd) {
            this(cmd,PRIORITY.MID,DEFTIMEOUT);
        }

        public byte[] getCmd() {
            return cmd;
        }

        public void setCmd(byte[] cmd) {
            this.cmd = cmd;
        }

        public byte[] getData() {
            return data;
        }

        public void setData(byte[] data) {
            this.data = data;
        }

        public PRIORITY getPriority() {
            return priority;
        }

        public void setPriority(PRIORITY priority) {
            this.priority = priority;
        }


        public long getTimeout() {
            return timeout;
        }

        public void setTimeout(long timeout) {
            this.timeout = timeout;
        }
        @Override
        public int compareTo(BtTaskPara other) {
            if(other.getPriority().ordinal() > this.getPriority().ordinal()){
                return 1;
            }else if(other.getPriority().ordinal() == this.getPriority().ordinal()) {
                return 0;
            }
            return -1;
        }

        @Override
        public String toString() {
            return "BtTaskPara = PRIORITY:" + priority.toString() + " COMMAND :" + DataUtil.printHexBinary(cmd) + " TimeOut :" + timeout + " CmdID : " + cmdID;
        }

        @Override
        protected Object clone() {
            BtTaskPara para = null;
            try{
                para = (BtTaskPara)super.clone();
            } catch (CloneNotSupportedException e) {
                e.printStackTrace();
            }
            return para;
        }
    }
    private static class TasksThread extends Thread{
        @Override
        public void run() {
            while (run){
                try {
                    if(taskQueue.isEmpty()){
                        Thread.sleep(60000);
                    }else {
                        taskstatus = TASKSTATUS.BUSY;
                        BtTaskPara para = taskQueue.poll();
                        curCmdID = para.getCmdID();
                        curPara = (BtTaskPara)para.clone();
                        if(curCmdID == DataUtil.COM_SEND_GET_NAME){
                            String mac = DataUtil.byteArrayToStr(curPara.getData());
                            addFIFOMac(mac);
                        }
                        LogUtil.d(TAG,para.toString());
                        serialPortUtil.sendBuffer(para.getCmd());
                        Thread.sleep(para.getTimeout());
                        LogUtil.d(TAG,"===========TIMEOUT============" + para.toString());
                        if(callback != null){
                            callback.OnTimeOutCallback(para.getCmdID());
                        }
                    }
                } catch (InterruptedException e) {
                    LogUtil.d(TAG,"===Interrupted===");
                }finally {
                    curPara = null;
                    curCmdID = DEF_CMD_ID;
                    if(taskQueue.isEmpty()) {
                        taskstatus = TASKSTATUS.FREE;
                    }
                }
            }
            LogUtil.d(TAG,"TasksThread---------------===========finish-------------============");
            taskQueue = null;
        }
    }
    public static void  setOnTaskCallback(OnTaskCallback tmpCallback){
        callback = tmpCallback;
    }
    public interface OnTaskCallback{
        void OnTimeOutCallback(int cmd);
    }
}
